using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Net;
using System.Collections;
using System.Xml;
using SMS.Common.ZipLib.Zip;
using SMS.Common.ZipLib.Checksums;


namespace SMS.Common.Remote
{
    ///<summary>
    /// Download or Upload file service.
    ///</summary>
    public class FileService
    {
        private readonly Uri internetUri;
        private readonly string localUri;

        ///<summary>
        /// Initial a new FileService class, with specified internet Uri and local file Uri.
        ///</summary>
        public FileService(string internetUri, string localUri)
        {
            this.internetUri = new Uri(internetUri);
            this.localUri = localUri;
        }

        ///<summary>
        /// Upload the local file to internet Uri.
        /// <param name="Zip">Set the Zip parameter to determine whether the local file should be compressed before transfer.</param>
        ///</summary>
        public ResponseStatus Upload(bool Zip)
        {
            bool Zipped = false;
            string zipFileUri = "";

            #region Test if the file is exist.
            FileInfo fi = new FileInfo(localUri);
            try
            {
                if (!fi.Exists) return ResponseStatus.FileNoFound;
            }
            catch (IOException e)
            {
                return ResponseStatus.SystemError;
            }
            #endregion

            #region If Zip == true Then Zip the local file.
            if (Zip)
            {
                try
                {
                    Crc32 crc = new Crc32();
                    ZipOutputStream outStream = new ZipOutputStream(File.Create(localUri + ".zip"));

                    outStream.SetLevel(9); // 0 - store only to 9 - means best compression

                    string file = localUri;

                    FileStream fs = File.OpenRead(localUri);

                    byte[] buffer = new byte[fs.Length];
                    fs.Read(buffer, 0, buffer.Length);
                    ZipEntry entry = new ZipEntry(file);

                    entry.DateTime = DateTime.Now;
                    entry.Size = fs.Length;
                    fs.Close();

                    crc.Reset();
                    crc.Update(buffer);

                    entry.Crc = crc.Value;

                    outStream.PutNextEntry(entry);

                    outStream.Write(buffer, 0, buffer.Length);

                    outStream.Finish();
                    outStream.Close();
                    zipFileUri = localUri + ".zip";
                    Zipped = true;
                }
                catch
                {
                    return ResponseStatus.SystemError;
                }
            }
            #endregion

            WebClient wc = new WebClient();
            try
            {
                if (Zipped)
                {
                    Uri tempUri = new Uri(internetUri.AbsoluteUri + "&FileSize=" + (new FileInfo(zipFileUri)).Length.ToString());
                    wc.UploadFile(tempUri, zipFileUri);
                }
                else
                {
                    Uri tempUri = new Uri(internetUri.AbsoluteUri + "&FileSize=" + (new FileInfo(localUri)).Length.ToString());
                    wc.UploadFile(tempUri, localUri);
                }

                if (wc.ResponseHeaders["PageStatus"] != "OK")
                {
                    if (wc.ResponseHeaders["PageStatus"] != null)
                    {
                        return GetResponseStatus(wc.ResponseHeaders["PageStatus"]);
                    }
                    else
                    {
                        return GetResponseStatus("NoResponse");
                    }
                }

                #region Remove ZipFile and Move Uploaded file to Uploaded folder.
                if (Zipped)
                {
                    FileInfo fi1 = new FileInfo(zipFileUri);
                    try
                    {
                        fi1.Delete();
                    }
                    catch
                    {
                    }
                }

                FileInfo fi2 = new FileInfo(localUri);
                try
                {
                    DirectoryInfo di1 = new DirectoryInfo(fi2.Directory.ToString() + "\\Uploaded\\");
                    if (!di1.Exists) di1.Create();
                    fi2.MoveTo(di1.ToString() + fi2.Name);
                }
                catch
                {
                }
                #endregion

                return ResponseStatus.OK;
            }
            catch
            {
                return ResponseStatus.SystemError;
            }

            # region User the code below to send data (not send a file) to server.
            //the server side should use Request.InputStream or Request.BinaryRead() to get the sent data.
            //Important: all sent data is not a file, instead of data stream.
            //http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnnetcomp/html/httpcomm.asp

            //HttpWebRequest request = (HttpWebRequest)WebRequest.Create(internetUri);
            //FileStream file = new FileStream(localUri, FileMode.Open);
            //FileInfo fi = new FileInfo(localUri);
            //if (!fi.Exists) return false;

            //request.Method = WebRequestMethods.Http.Put;
            //request.AllowWriteStreamBuffering = true;

            //Stream sendStream = request.GetRequestStream();

            //int readLength = 1024;
            //byte[] readBuffer = new byte[1024];
            //long fileSizeInByters = file.Length;
            //int bytersRead = 0;

            //bytersRead = file.Read(readBuffer, 0, readLength);
            //while (bytersRead > 0)
            //{
            //    sendStream.Write(readBuffer, 0, bytersRead);
            //    fileSizeInByters -= bytersRead;
            //    //Here we can report fileSizeInByters for remaind.
            //    bytersRead = file.Read(readBuffer, 0, readLength);
            //}
            //file.Close();
            //sendStream.Close();
            //request.GetResponse();
            //return true;
            //}
            //catch (WebException e)
            //{
            //    return false;
            //}
            //catch (IOException e)
            //{
            //    return false;
            //}
            //catch (ArgumentException e)
            //{
            //    return false;
            //}
            //catch (System.Security.SecurityException e)
            //{
            //    return false;
            //}
            //catch (UnauthorizedAccessException e)
            //{
            //    return false;
            //}
            //catch (HttpListenerException e)
            //{
            //    return false;
            //}
            #endregion
        }

        ///<summary>
        /// Upload the local file to internet Uri with no compress.
        ///</summary>
        public ResponseStatus Upload()
        {
            return this.Upload(false);
        }

        ///<summary>
        /// Upload the internet file, and save to local Uri.
        ///</summary>
        public ResponseStatus Download()
        {
            //try
            //{
            WebClient wc = new WebClient();
            wc.DownloadFile(internetUri, localUri + ".tmp");
            if (wc.ResponseHeaders["PageStatus"] != "OK")
            {
                FileInfo fi = new FileInfo(localUri + ".tmp");
                try
                {
                    fi.Delete();
                }
                catch
                { }
                if (wc.ResponseHeaders["PageStatus"] != null)
                {
                    return GetResponseStatus(wc.ResponseHeaders["PageStatus"]);
                }
                else
                {
                    return GetResponseStatus("NoResponse");
                }
            }
            else
            {
                FileInfo fi = new FileInfo(localUri + ".tmp");
                if (!(fi.Length == long.Parse(wc.ResponseHeaders["FileSize"]))) return ResponseStatus.DataError;
                FileInfo fi1 = new FileInfo(localUri);
                if (fi1.Exists) fi1.Delete();
                fi.MoveTo(localUri);
                return ResponseStatus.OK;
            }
            //}
            //catch (WebException e)
            //{
            //    return ResponseStatus.SystemError;
            //}
            //catch (IOException e)
            //{
            //    return ResponseStatus.SystemError;
            //}

            #region Use the code below to download data (not suit for download file)
            //string[] tmpStrings1 = localUri.Split(new char[] { '\\' });
            //string path = "";
            //for (int i = 0; i < tmpStrings1.Length - 1; i++)
            //{
            //    path += tmpStrings1[i];
            //}
            //path += "\\";

            //if (path.Trim().Length == 0)
            //{
            //    path = AppDomain.CurrentDomain.BaseDirectory;
            //}

            //DirectoryInfo di = new DirectoryInfo(path);
            //try
            //{
            //    if (!di.Exists) di.Create();
            //}
            //catch
            //{
            //    return false;
            //}

            //int readLength = 1024;
            //byte[] readBuffer = new byte[1024];
            //long fileSizeInByters;
            //int bytersRead = 0;

            //WebRequest request = (WebRequest)WebRequest.Create(this.internetUri);
            //request.Credentials = CredentialCache.DefaultCredentials;

            //try
            //{
            //    HttpWebResponse response = (HttpWebResponse)request.GetResponse();
            //    if (response.StatusCode == HttpStatusCode.OK)
            //    {
            //        if (response.GetResponseHeader("PageStatus") != "OK") return false;

            //        FileStream fileStream = new FileStream(localUri, FileMode.Create, FileAccess.Write, FileShare.ReadWrite);
            //        Stream reader = response.GetResponseStream();
            //        //Total Byters of file.
            //        fileSizeInByters = response.ContentLength;

            //        //Read Byters of file.
            //        bytersRead = reader.Read(readBuffer, 0, readLength);

            //        while (bytersRead > 0)
            //        {
            //            fileStream.Write(readBuffer, 0, bytersRead);
            //            fileSizeInByters -= bytersRead;
            //            //Here we can report fileSizeInByters for remaind.
            //            bytersRead = reader.Read(readBuffer, 0, readLength);
            //        }
            //        reader.Close();
            //        fileStream.Close();
            //        response.Close();
            //        return true;
            //    }
            //    else
            //    {
            //        response.Close();
            //        return false;
            //    }
            //}
            //catch (WebException e)
            //{
            //    return false;
            //}
            //catch (IOException e)
            //{
            //    return false;
            //}
            //catch (ArgumentException e)
            //{
            //    return false;
            //}
            //catch (System.Security.SecurityException e)
            //{
            //    return false;
            //}
            //catch (UnauthorizedAccessException e)
            //{
            //    return false;
            //}
            #endregion
        }

        private ResponseStatus GetResponseStatus(string status)
        {
            switch (status.ToUpper())
            {
                case "OK":
                    return ResponseStatus.OK;
                case "STORENOFOUND":
                    return ResponseStatus.StoreNoFound;
                case "FILETYPEERROR":
                    return ResponseStatus.FileTypeError;
                case "FILENAMEERROR":
                    return ResponseStatus.FileNameError;
                case "FILENOFOUND":
                    return ResponseStatus.FileNoFound;
                case "DATAERROR":
                    return ResponseStatus.DataError;
                case "PAGEERROR":
                    return ResponseStatus.PageError;
                case "NORESPONSE":
                    return ResponseStatus.NoResponse;
                default:
                    return ResponseStatus.SystemError;
            }
        }

        private string GetResponseStatus(ResponseStatus status)
        {
            switch (status)
            {
                case ResponseStatus.OK:
                    return "OK";
                case ResponseStatus.StoreNoFound:
                    return "StoreNoFound";
                case ResponseStatus.FileTypeError:
                    return "FileTypeError";
                case ResponseStatus.FileNameError:
                    return "FileNameError";
                case ResponseStatus.FileNoFound:
                    return "FileNoFound";
                case ResponseStatus.DataError:
                    return "DataError";
                case ResponseStatus.PageError:
                    return "PageError";
                case ResponseStatus.NoResponse:
                    return "NoResponse";
                default:
                    return "SystemError";
            }
        }
    }

    public enum ResponseStatus
    {
        OK,
        StoreNoFound,
        FileTypeError,
        FileNameError,
        FileNoFound,
        DataError,
        PageError,
        SystemError,
        NoResponse
    }
}